"Badges" in Sway Window Titles

I have been using network namespaces and I thought it would be cool to be able to tell the namespace of a window by just looking at it. This post explores how to do that in sway.

Instead of showing how to do it for network namespaces, a slightly more general version is developed where the colour is chosen according to some condition on the pid of the window.

I originally intended to change the windows title bar and border colours. Unfortunately this feature is not available and it seems it will never be.

It is nevertheless possible to have custom background colours for the title, so it looks like a badge, using pango markup with sway's title_format command.

This can be achieved with the following steps

  1. Subscribe to window events
  2. Extract program's pid when a new window is created
  3. Change the window title format depending on some condition on the pid

For step 1, you can monitor sway events with the swaymsg command. We are interested in window events. The following command prints to stdout information about window events as they occur

swaymsg -mt subscribe '["window"]'

In step 2, it is necessary to process the output from step 1. Since it is in JSON format jq is the perfect tool for the job.

In order to filter new window events from the output, take events for which the change field is "new" and extract the corresponding pid.

swaymsg -mt subscribe '["window"]' |
    jq --unbuffered  'select(.change == "new") | .container.pid'

The --unbuffered option is necessary for the pid to be printed as soon as the window appears.

For step 3, the title can be customized using sway's title_format command. To be able to change the background colour you have to enable pango with sway's font command. For example

swaymsg 'font pango:monospace 10'

You can use the font of your preference instead of just 'monospace'. If you would like pango to be enabled by default when sway starts add the font command to your sway configuration file.

Now you can use pango markup to modify the format of the title. The following script runs a new foot process, saves the pid and uses for_window and title_format to set the background colour once the window shows on the screen.

#!/usr/bin/bash

foot &
pid=$!

swaymsg "for_window [pid=\"$pid\"] title_format \"<span background='#ff0000'>  %title  </span>\""

The idea now is to read each line in the output of step 2, which corresponds to the pid of the most recent new window, check some condition on that pid and if it is satisfied run something on the lines of the swaymsg command above.

An important change in the final version is to take away for_window. This is because when for_window is used it applies to new windows and when it is not used it applies to existing windows. In the foot example the window had not yet appeared when the pid was captured.

Let's say we want all new windows with an even pid to have a title with a green background. Here is a way to do it

#!/usr/bin/bash

color="#00ff00"

swaymsg -mt subscribe '["window"]' |
    jq --unbuffered  'select(.change == "new") | .container.pid' |
    while read -r pid
    do
	if [ $(($pid % 2)) -eq 0 ]
	then
	    swaymsg "[pid=\"$pid\"] title_format \"<span background='$color'>  %title  </span>\""
	fi
    done

While the script runs, open some terminals and note that only some of them (those with an even pid) have a green background in the title.

You can modify the condition above so that you can assign different colours on more interesting conditions. My original purpose was to have different colours for different namespaces. If you are looking to do something similar the way to get the name of the network namespace for a particular pid is

ip netns identify <pid>